/*
 * DenseVector.cs
 *
 * Copyright (c) 2005, dnAnalytics. All rights reserved.
 */

#region Using directives
using System;
using System.Text;
using System.Collections;
using System.Collections.Generic;
using dnAnalytics;
using dnAnalytics.Exceptions;
using dnAnalytics.Resources;
#endregion Using directives

namespace dnAnalytics.LinearAlgebra
{
    /// <summary>
    /// A dense implementation of <see cref="Vector"/>.
    /// </summary>
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")]
    [Serializable]
    internal sealed class DenseVector : Vector
    {
        #region Fields
        private readonly ISimplifiedBlas blas;
        /// <summary>
        /// Internal data for the vector.
        /// </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields")]
        internal double[] data;

        #endregion Fields

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <b>Vector</b> class.
        /// </summary>
        public DenseVector(int length, bool useNativeBlas)
            : base(length)
        {
            if (useNativeBlas)
            {
                blas = new NativeBlas();
            }
            else
            {
                blas = new ManagedBlas();
            }
            data = new double[length];
        }

        /// <summary> 
        /// Constructs a vector with the given size and with each element set the given value. 
        /// </summary> 
        /// <param name="length">the size of the vector.</param> 
        /// <param name="value">the value to set each element to.</param> 
        /// <param name="useNativeBlas">use native BLAS routines.</param> 
        /// <exception cref="ArgumentException">If length is less than 1.</exception> 
        public DenseVector(int length, double value, bool useNativeBlas)
            : this(length, useNativeBlas)
        {
            for (int i = 0; i < length; i++)
            {
                data[i] = value;
            }
        }

        /// <summary>
        /// Create instance from Vector interface.
        /// </summary>
        /// <param name="source">The <b>Vector</b> to create this vector from.</param>
        /// <exception cref="ArgumentNullException">If source is <b>null</b>.</exception>
        public DenseVector(Vector source)
            : this(source.Count, Settings.UseNativeLibrary)
        {
            foreach (KeyValuePair<int, double> element in source)
            {
                data[element.Key] = element.Value;
            }
        }

        /// <summary>
        /// Copy Constructor.
        /// </summary>
        /// <param name="source">The <b>DenseVector</b> to create this vector from.</param>
        /// <exception cref="ArgumentNullException">If source is <b>null</b>.</exception>
        private DenseVector(DenseVector source)
            : base(source.Count)
        {
            data = new double[source.data.Length];
            Buffer.BlockCopy(source.data, 0, data, 0, source.data.Length * Constants.SizeOfDouble);
            this.blas = source.blas;
        }

        public DenseVector(double[] source, bool useNativeBlas)
            : this(source.Length, useNativeBlas)
        {
            Buffer.BlockCopy(source, 0, data, 0, source.Length * Constants.SizeOfDouble);
        }

        #endregion Constructors

        #region Properties

        /// <summary>Indexer gets or sets the value at the given <paramref name="index"/>.</summary>
        /// <param name="index">the index of the value to get or set.</param>
        /// <returns>The value of the vector at the given <paramref name="index"/>.</returns>        
        /// <exception cref="IndexOutOfRangeException">It <paramref name="index"/> is negative or 
        /// greater than The size of the vector.</exception>
        public override double this[int index]
        {
            get
            {
                return data[index];
            }
            set
            {
                data[index] = value;
            }
        }

        #endregion Properties

        #region Protected Methods
        /// <summary>
        /// Creates a <b>Vector</b> of the given size;
        /// </summary>
        /// <param name="size">The size of the <b>Vector</b> to create.</param>
        /// <returns>The new <b>Vector</b>.</returns>
        protected override Vector CreateVector(int size)
        {
            return VectorBuilder.CreateVector(size, VectorType.Dense);
        }
        #endregion

        #region public override Methods
        /// <summary>Calculates the infinity norm of this vector.</summary>
        /// <returns>Ihe infinity norm of this vector.</returns>  
        public override double InfinityNorm()
        {
            double ret = 0;
            for (int i = 0; i < data.Length; i++)
            {
                ret = System.Math.Max(ret, System.Math.Abs(data[i]));
            }
            return ret;
        }

        /// <summary>Calculates the L1 norm of this vector.</summary>
        /// <returns>The L1 norm of this vector.</returns>        
        public override double L1Norm()
        {
            double ret = 0;
            for (int i = 0; i < data.Length; ++i)
            {
                ret += System.Math.Abs(data[i]);
            }
            return ret;
        }

        /// <summary>Calculates the L2 norm of this vector.</summary>
        /// <returns>The L2 norm of this vector.</returns>        
        public override double L2Norm()
        {
            double ret = 0;
            for (int i = 0; i < data.Length; ++i)
            {
                ret += data[i] * data[i];
            }
            return System.Math.Sqrt(ret);
        }

        /// <summary>
        /// Returns the value of the absolute maximum element.
        /// </summary>
        /// <returns>the value of the absolute maximum element.</returns>
        public override double AbsoluteMaximum()
        {
            return System.Math.Abs(data[AbsoluteMaximumIndex()]);
        }

        /// <summary>
        /// Returns the index of the absolute maximum element.
        /// </summary>
        /// <returns>The index of absolute maximum element.</returns>        
        public override int AbsoluteMaximumIndex()
        {
            int index = 0;
            double max = System.Math.Abs(data[index]);
            for (int i = 1; i < data.Length; i++)
            {
                double test = System.Math.Abs(data[i]);
                if (test > max)
                {
                    index = i;
                    max = test;
                }
            }
            return index;
        }

        /// <summary>
        /// Returns the value of the absolute minimum element.
        /// </summary>
        /// <returns>The value of the absolute minimum element.</returns>
        public override double AbsoluteMinimum()
        {
            return System.Math.Abs(data[AbsoluteMinimumIndex()]);
        }

        /// <summary>
        /// Returns the index of the absolute minimum element.
        /// </summary>
        /// <returns>The index of absolute minimum element.</returns>        
        public override int AbsoluteMinimumIndex()
        {
            int index = 0;
            double min = System.Math.Abs(data[index]);
            for (int i = 1; i < data.Length; i++)
            {
                double test = System.Math.Abs(data[i]);
                if (test < min)
                {
                    index = i;
                    min = test;
                }
            }
            return index;
        }

        /// <summary>
        /// Returns the value of maximum element.
        /// </summary>
        /// <returns>The value of maximum element.</returns>        
        public override double Maximum()
        {
            return data[MaximumIndex()];
        }

        /// <summary>
        /// Returns the index of the absolute maximum element.
        /// </summary>
        /// <returns>The index of absolute maximum element.</returns>          
        public override int MaximumIndex()
        {
            int index = 0;
            double max = data[0];
            for (int i = 1; i < data.Length; i++)
            {
                if (max < data[i])
                {
                    index = i;
                    max = data[i];
                }
            }
            return index;
        }

        /// <summary>
        /// Returns the value of the minimum element.
        /// </summary>
        /// <returns>The value of the minimum element.</returns>
        public override double Minimum()
        {
            return data[MinimumIndex()];
        }

        /// <summary>
        /// Returns the index of the minimum element.
        /// </summary>
        /// <returns>The index of minimum element.</returns>  
        public override int MinimumIndex()
        {
            int index = 0;
            double min = data[0];
            for (int i = 1; i < data.Length; i++)
            {
                if (min > data[i])
                {
                    index = i;
                    min = data[i];
                }
            }
            return index;
        }

        /// <summary>
        /// Sets the each element to zero.
        /// </summary>
        public override void Clear()
        {
            Array.Clear(data, 0, data.Length);
        }

        /// <summary>
        /// Copies the values of this vector into the target vector.
        /// </summary>
        /// <param name="target">The vector to copy elements into.</param>
        /// <exception cref="ArgumentNullException">If <paramref name="target"/> is <b>null</b>.</exception>
        /// <exception cref="NotConformableException">If <paramref name="target"/> is not the same size as this vector.</exception>
        public override void CopyTo(Vector target)
        {
            if (target == null)
            {
                throw new ArgumentNullException("target", Strings.NullParameterException);
            }
            if (this.Count != target.Count)
            {
                throw new NotConformableException("target", Strings.ParameterNotConformable);
            }
            target.SetValues(data);
        }

        /// <summary>Computes the dot product of this vector with another.</summary>
        /// <param name="other">The vector to compute the dot product with.</param>
        /// <returns>The dot product of this vector and other.</returns>
        /// <exception cref="ArgumentNullException">If the other vector is <b>null</b>.</exception>
        /// <exception cref="NotConformableException">If this vector the other vector are not the same size.</exception>
        public override double DotProduct(Vector other)
        {
            if (other == null)
            {
                throw new ArgumentNullException("other", Strings.NullParameterException);
            }
            if (this.Count != other.Count)
            {
                throw new NotConformableException("other", Strings.ParameterNotConformable);
            }

            DenseVector dense = other as DenseVector;
            if (dense == null)
            {
                return base.DotProduct(other);
            }
            else
            {
                return blas.DotProduct(data, dense.data);
            }
        }

        /// <summary>
        /// Returns an <see cref="IEnumerator{T}"/>  over this vector.
        /// </summary>
        /// <returns>An <see cref="IEnumerator{T}"/> over this vector.</returns>
        /// <remarks>The enumerator returns a <seealso cref="KeyValuePair{T,K}"/> with the key being the element index and the value 
        /// being the value of the element at that index.</remarks>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")]
        public override IEnumerator<KeyValuePair<int, double>> GetEnumerator()
        {
            for (int i = 0; i < data.Length; i++)
            {
                yield return new KeyValuePair<int, double>(i, data[i]);
            }
        }

        /// <summary>
        /// Returns an enumerator over a <see cref="Range"/> of this vector.
        /// </summary>
        /// <param name="range">The range of elements to enumerate over.</param>
        /// <returns>An enumerator over a range of this vector.</returns>
        /// <exception cref="ArgumentOutOfRangeException">If <paramref name="range"/> specifies a range
        /// outside the vector's range. </exception>
        /// <remarks>The enumerator returns a <seealso cref="KeyValuePair{T,K}"/> with the key being the element index and the value 
        /// being the value of the element at that index.</remarks>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")]
        public override IEnumerator<KeyValuePair<int, double>> GetEnumerator(Range range)
        {
            if (range.End > data.Length || range.Start > data.Length)
            {
                throw new ArgumentOutOfRangeException("range");
            }

            for (int i = range.Start; i <= range.End; i += range.Stride)
            {
                yield return new KeyValuePair<int, double>(i, data[i]);
            }
        }

        /// <summary>
        /// Creates a vector containing copy of elements for the given <see cref="Range"/>.
        /// </summary>
        /// <param name="range">The <see cref="Range"/> of elements to create a new vector from.</param>
        /// <returns>A vector containing a copy of elements for the given <see cref="Range"/>.</returns>
        /// <exception cref="ArgumentOutOfRangeException">If the range is outside The size of the vector.</exception>        
        public override Vector SubVector(Range range)
        {
            if (range.End > data.Length || range.Start > data.Length)
            {
                throw new ArgumentOutOfRangeException("range");
            }

            int num = (range.End - range.Start) / range.Stride + 1;
            DenseVector ret = new DenseVector(num, Settings.UseNativeLibrary);
            for (int i = range.Start, j = 0; i <= range.End; i += range.Stride, j++)
            {
                ret.data[j] = data[i];
            }
            return ret;
        }

        /// <summary>
        /// Computes the sum of the elements of this vector.
        /// </summary>
        /// <returns>The sum of the elements of this vector.</returns>
        public override double Sum()
        {
            double ret = 0;
            for (int i = 0; i < data.Length; i++)
            {
                ret += data[i];
            }
            return ret;
        }

        /// <summary>
        /// Computes the sum of the absolute value of the elements of this vector.
        /// </summary>
        /// <returns>The sum of the absolute value of the elements of this vector.</returns>
        public override double SumMagnitudes()
        {
            return blas.SumMagnitudes(data);
        }

        /// <summary>
        /// Swaps the values of this vector with another vector.
        /// </summary>
        /// <param name="other">The vector to swap data with.</param>
        /// <exception cref="ArgumentNullException">If the other vector is <b>null</b>.</exception>
        /// <exception cref="NotConformableException">If this vector and <paramref name="other"/> are not the same size.</exception>
        public override void Swap(Vector other)
        {
            if (other == null)
            {
                throw new ArgumentNullException("other", Strings.NullParameterException);
            }
            if (this.Count != other.Count)
            {
                throw new NotConformableException("other", Strings.ParameterNotConformable);
            }

            double[] temp = other.ToArray();
            other.SetValues(data);
            this.data = temp;
        }

        /// <summary>
        /// Returns the data contained in the vector as an array.
        /// </summary>
        /// <returns>The data as an array.</returns>        
        public override double[] ToArray()
        {
            double[] ret = new double[data.Length];
            Buffer.BlockCopy(data, 0, ret, 0, data.Length * Constants.SizeOfDouble);
            return ret;
        }

        /// <summary>
        /// Set the values of this vector to the given values.
        /// </summary>
        /// <param name="values">The array containing the values to use.</param>
        /// <exception cref="ArgumentNullException">If <paramref name="values"/> is <b>null</b>.</exception>
        /// <exception cref="NotConformableException">If <paramref name="values"/> is not the same size as this vector.</exception>
        public override void SetValues(double[] values)
        {
            if (values == null)
            {
                throw new ArgumentNullException("values", Strings.NullParameterException);
            }
            if (values.Length != data.Length)
            {
                throw new NotConformableException("values", Strings.ArrayParameterNotConformable);
            }

            Buffer.BlockCopy(values, 0, data, 0, values.Length * Constants.SizeOfDouble);
        }

        /// <summary>
        /// Adds a scalar to each element of the vector.
        /// </summary>
        /// <param name="scalar">The scalar to add.</param>
        public override void Add(double scalar)
        {
            blas.Add(data, scalar);
        }

        /// <summary>
        /// Adds another vector to this vector.
        /// </summary>
        /// <param name="other">The vector to add to this one.</param>
        /// <exception cref="ArgumentNullException">If the other vector is <b>null</b>.</exception> 
        /// <exception cref="NotConformableException">If this vector and <paramref name="other"/> are not the same size.</exception>
        public override void Add(Vector other)
        {
            if (other == null)
            {
                throw new ArgumentNullException("other", Strings.NullParameterException);
            }
            if (this.Count != other.Count)
            {
                throw new NotConformableException("other", Strings.ParameterNotConformable);
            }

            DenseVector dense = other as DenseVector;
            if (dense == null)
            {
                base.Add(other);
            }
            else
            {
                blas.Add(data, dense.data);
            }
        }

        /// <summary>
        /// Negates the values of this vector.
        /// </summary>
        public override void Negate()
        {
            blas.Scale(-1, data);
        }

        /// <summary>
        /// Subtracts a scalar from each element of the vector.
        /// </summary>
        /// <param name="scalar">Ihe scalar to subtract.</param>
        public override void Subtract(double scalar)
        {
            blas.Subtract(data, scalar);
        }

        /// <summary>
        /// Subtracts another vector from this vector.
        /// </summary>
        /// <param name="other">The vector to subtract from this one.</param>
        /// <exception cref="ArgumentNullException">If the other vector is <b>null</b>.</exception> 
        /// <exception cref="NotConformableException">If this vector and <paramref name="other"/> are not the same size.</exception>
        public override void Subtract(Vector other)
        {
            if (other == null)
            {
                throw new ArgumentNullException("other", Strings.NullParameterException);
            }
            if (this.Count != other.Count)
            {
                throw new NotConformableException("other", Strings.ParameterNotConformable);
            }

            DenseVector dense = other as DenseVector;
            if (dense == null)
            {
                base.Subtract(other);
            }
            else
            {
                blas.Subtract(data, dense.data);
            }
        }

        /// <summary>
        /// Divides this vector by scalar.
        /// </summary>
        /// <param name="scalar">The scalar to divide this vector by.</param>
        public override void Divide(double scalar)
        {
            blas.Scale(1 / scalar, data);
        }

        /// <summary>
        /// Divides this vector by scalar and store the result in the result vector.
        /// </summary>
        /// <param name="scalar">The scalar to divide this vector by.</param>
        /// <param name="result">The vector to store the result of the division.</param>
        /// <exception cref="ArgumentNullException">If the result vector is <b>null</b>.</exception> 
        /// <exception cref="NotConformableException">If this vector and <paramref name="result"/> are not the same size.</exception>
        public override void Divide(double scalar, Vector result)
        {
            if (result == null)
            {
                throw new ArgumentNullException("result", Strings.NullParameterException);
            }
            if (this.Count != result.Count)
            {
                throw new NotConformableException("result", Strings.ParameterNotConformable);
            }

            this.CopyTo(result);
            result.Divide(scalar);
        }

        /// <summary>
        /// Multiplies this vector by scalar.
        /// </summary>
        /// <param name="scalar">The scalar to multiply this vector with.</param>
        public override void Multiply(double scalar)
        {
            blas.Scale(scalar, data);
        }

        /// <summary>
        /// Multiplies this vector with another vector (this x other) and returns the resulting matrix.
        /// </summary>
        /// <param name="rightSide">The vector to multiply the vector with.</param>
        /// <exception cref="ArgumentNullException">If <paramref name="rightSide"/> is <b>null</b>.</exception> 
        public override Matrix Multiply(Vector rightSide)
        {
            if (rightSide == null)
            {
                throw new ArgumentNullException("rightSide", Strings.NullParameterException);
            }

            DenseVector dense = rightSide as DenseVector;
            if (dense == null)
            {
                return base.Multiply(rightSide);
            }
            else
            {
                DenseMatrix result = new DenseMatrix(this.Count, rightSide.Count, Settings.UseNativeLibrary);
                blas.Multiply(this.Count, rightSide.Count, 1, data, dense.data, result.data);
                return result;
            }
        }

        /// <summary>
        /// Multiplies this vector with another vector (this x other) and places the result into the result vector.
        /// </summary>
        /// <param name="rightSide">The vector to multiply the vector with.</param>
        /// <param name="result">The result of the multiplication.</param>
        /// <exception cref="ArgumentNullException">If <paramref name="rightSide"/> is <b>null</b>.</exception> 
        /// <exception cref="ArgumentNullException">If the result vector is <b>null</b>.</exception> 
        public override void Multiply(Vector rightSide, Matrix result)
        {
            if (rightSide == null)
            {
                throw new ArgumentNullException("rightSide", Strings.NullParameterException);
            }
            if (result == null)
            {
                throw new ArgumentNullException("result", Strings.NullParameterException);
            }
            if (result.Rows != this.Count || result.Columns != rightSide.Count)
            {
                throw new NotConformableException("result", Strings.ResultMatrixIncorrectDimensions);
            }

            if (rightSide.GetType() == typeof(DenseVector) && result.GetType() == typeof(DenseMatrix))
            {
                DenseVector rightVector = (DenseVector)rightSide;
                DenseMatrix resultMatrix = (DenseMatrix)result;
                blas.Multiply(this.Count, rightSide.Count, 1, data, rightVector.data, resultMatrix.data);
            }
            else
            {
                base.Multiply(rightSide, result);
            }
        }

        /// <summary>
        /// Returns a deep-copy clone of the <b>Vector</b>.
        /// </summary>
        /// <returns>A deep-copy clone of the <b>Vector</b>.</returns>
        public override Vector Clone()
        {
            return new DenseVector(this);
        }

        #endregion Public Methods
    }
}